package com.zgw.fireline.base.dataset;

import java.math.BigDecimal;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.EventObject;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sql.rowset.CachedRowSet;

import com.sun.rowset.CachedRowSetImpl;
import com.sun.rowset.internal.Row;
import com.zgw.fireline.base.DatasetDefine;
import com.zgw.fireline.base.IDataBaseProvide;
import com.zgw.fireline.base.common.Assert;

/**
 * 数据集
 * */
public class Dataset {
	protected static final int CURSOR_MOVED = 1;
	protected static final int ROW_CHANGED = 2;
	protected static final int ROW_SET_CHANGED = 3;
	private String[] keyColumns;
	private CachedRowSet rowset = null;// 数据核心存储器
	private List<DatasetListener> listeners;
	String command;
	Object[] params = new Object[0];
	String defineKey; // 系统数据源定义key
	public final boolean sysSource;
	final IDataBaseProvide provide; // 底层数据库交互接口
	private int selectedIndex = -1; // 指向-1 表示未选中行

	public Dataset(String sqlCommand, IDataBaseProvide provide) {
		sysSource = false;
		this.command = sqlCommand;
		this.provide = provide;
	}

	public Dataset(boolean systemSource, String defineKey,
			IDataBaseProvide provide) {
		this.sysSource = systemSource;
		this.defineKey = defineKey;
		this.provide = provide;
	}

	// 触发事件
	private void fireDatasetEvent(int type, EventObject event) {
		if (listeners == null)
			listeners = new ArrayList<DatasetListener>();
		if (type == CURSOR_MOVED) {
		}
		for (DatasetListener l : listeners) {
			if (type == ROW_CHANGED)
				l.rowChanged(event);
			if (type == CURSOR_MOVED)
				l.cursorMoved(event);
			if (type == ROW_SET_CHANGED)
				l.rowSetChanged(event);
		}
	}

	// 添加监听
	public void addDatasetListener(DatasetListener listener) {
		if (listeners == null)
			listeners = new ArrayList<DatasetListener>();
		if (!listeners.contains(listener))
			listeners.add(listener);
	}

	// 移除监听
	public void removeDatasetListener(DatasetListener listener) {
		if (listeners == null)
			return;
		if (listeners.contains(listener))
			listeners.remove(listener);
	}

	// 设置主键列
	public void setKeyColumns(String[] keyColumns) {
		this.keyColumns = keyColumns;

	}

	public void populate(Dataset source) {
		// 验证与填充源的定义是否相等。
		Assert.isTrue(source.provide.getClass() == this.provide.getClass());
		Assert.isTrue(source.sysSource == this.sysSource);
		if (sysSource)
			Assert.isTrue(source.defineKey.equals(this.defineKey));
		else
			Assert.isTrue(source.command.equals(this.command));

		try {
			selectedIndex = -1;
			rowset = new CachedRowSetImpl();
			if (source.rowset != null)
				rowset.populate(source.rowset);
			fireDatasetEvent(ROW_SET_CHANGED, new EventObject(this));
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	// 填充source指定索引行
	public void populate(Dataset source, int index) {
		// 验证与填充源的定义是否相等。
		Assert.isTrue(source.provide.getClass() == this.provide.getClass());
		Assert.isTrue(source.sysSource == this.sysSource);
		if (sysSource)
			Assert.isTrue(source.defineKey.equals(this.defineKey));
		else
			Assert.isTrue(source.command.equals(this.command));
		// 索引是否有效
		if (index < 0 || index >= source.getCount())
			throw new IndexOutOfBoundsException("非法索引:" + index + "总数："
					+ getCount());

		try {
			selectedIndex = -1;
			rowset = new CachedRowSetImpl();
			source.rowset.absolute(index + 1);
			rowset.populate(source.rowset.getOriginalRow());
			fireDatasetEvent(ROW_SET_CHANGED, new EventObject(this));
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public Object[][] toArray() {
		if (rowset == null)
			return new Object[0][0];
		try {
			Collection<?> con = rowset.toCollection();
			Object[][] datas = new Object[con.size()][getColumnCount()];
			int i = 0;
			for (Object o : con) {
				Row r = (Row) o;
				for (int k = 0; k < datas[i].length; k++) {
					datas[i][k] = r.getColumnObject(k + 1);
				}
				i++;
			}
			return datas;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public <T> Collection<T> toCollection(Class<T> type) {
		if (rowset == null)
			return null;
		try {
			return (Collection<T>) rowset.toCollection();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public int indexOf(String column) {
		String[] columns = getColumns();
		for (int i = 0; i < columns.length; i++) {
			if (column.equalsIgnoreCase(columns[i])) {
				return i;
			}
		}
		return -1;
	}

	public void update() {
		Object[] newParams = new Object[params.length];
		int i = 0;
		for (Object o : params) {
			newParams[i] = o;
			i++;
		}
		try {
			if (sysSource) {
				Assert.isTrue(
						defineKey != null && !defineKey.trim().equals(""),
						"资源编号不能为空");
				rowset = provide.queryForDefine(defineKey, newParams);
			} else {
				Assert.isTrue(command != null && !command.trim().equals(""),
						"Sql语句不能为空");
				rowset = provide.queryForSql(command, newParams);
			}
			selectedIndex = -1;
			fireDatasetEvent(ROW_SET_CHANGED, new EventObject(this));
		} catch (Exception e) {
			throw new RuntimeException("数据集更新失败", e);
		}
	}

	public static String toString(Object obj, String format) {
		if (obj instanceof String) {
			return (String) obj;
		} else if (obj instanceof Date) {
			format = format == null ? "yyyy-MM-dd HH:mm:ss" : format;
			return new SimpleDateFormat(format).format(obj);
		} else if (obj instanceof Double || obj instanceof Float
				|| obj instanceof BigDecimal) {
			format = format == null ? ".00" : format;
			DecimalFormat dec = new DecimalFormat();
			dec.applyPattern(format);
			return dec.format(obj);
		} else if (obj != null) {
			return String.valueOf(obj);
		}
		return "";
	}

	/**
	 * Dataset是否通过 {@link update} 方法 或{@link populate }方法实例化数据
	 * */
	public boolean isInstantiation() {
		return rowset != null;
	}

	// 获取所有列
	public String[] getColumns() {
		if (rowset == null)
			return null;
		try {
			ResultSetMetaData meta = rowset.getMetaData();
			String[] columns = new String[meta.getColumnCount()];
			for (int i = 1; i <= meta.getColumnCount(); i++) {
				columns[i - 1] = meta.getColumnName(i);
			}
			return columns;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	// 获取列类别
	public Class<?> getType(String column) {
		if (rowset == null)
			return null;
		try {
			ResultSetMetaData meta = rowset.getMetaData();
			for (int i = 1; i <= meta.getColumnCount(); i++) {
				if (meta.getColumnName(i).equalsIgnoreCase(column))
					return Class.forName(meta.getColumnClassName(i));
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	// 获取主键列
	public String[] getKeyColumns() {
		return keyColumns;
	}

	// 获取总行数
	public int getCount() {
		if (rowset == null)
			return 0;
		return rowset.size();
	}

	public int getColumnCount() {
		if (rowset == null)
			return 0;
		try {
			return rowset.getMetaData().getColumnCount();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public String getColumnName(int index) {
		return getColumns()[index];
	}

	// 获取当前行号
	public int getSelectedIndex() {
		return selectedIndex;
	}

	// 根据行号，选中指定行
	public void setSelected(int index) {
		if (index == selectedIndex)
			return;
		if (index < -1 || index >= getCount())
			throw new IndexOutOfBoundsException("非法索引:" + index + " 总行数："
					+ getCount());
		this.selectedIndex = index;
		fireDatasetEvent(CURSOR_MOVED, new EventObject(this));
	}

	// 选中定位
	public void setSelected(Object... keyValues) {
		if (rowset == null)
			return;
		Assert.isNotNull(keyValues);
		Assert.isTrue(keyColumns != null && keyColumns.length > 0, "未设置主键列");
		Assert.isTrue(keyColumns.length == keyValues.length);
		int i = 0;
		Object value;
		selectedIndex = -1;
		for (i = 0; i < getCount(); i++) {
			boolean check = true;
			for (int k = 0; k < keyColumns.length; k++) {
				if (keyValues[k] == null) {//
					check = false;
					break;
				}
				value = getValueForIndex(i, keyColumns[k],
						keyValues[k].getClass());
				check &= value != null && value.equals(keyValues[k]);
			}
			if (check) {
				setSelected(i);
				break;
			}
		}
	}

	public String getString(String column) {
		return getValue(column, String.class);
	}

	// 获取当前值
	public Object getValue(String column) {
		return getValueForIndex(getSelectedIndex(), column, Object.class);
	}

	// 获取当前值并转换至指定类型
	public <T> T getValue(String column, Class<T> type, T defaultValue) {
		T val = getValueForIndex(getSelectedIndex(), column, type);
		return val != null ? val : defaultValue;
	}

	public <T> T getValue(String column, Class<T> type) {
		return getValueForIndex(getSelectedIndex(), column, type);
	}

	// 获取指定索引指定列的值默认类型的值
	public Object getValueForIndex(int index, String column) {
		return getValueForIndex(index, column, Object.class);
	}

	@SuppressWarnings("unchecked")
	public <T> T getValueForIndex(int index, String column, Class<T> c) {
		if (rowset == null || index == -1)
			return null;
		if (index < 0 || index >= getCount())
			throw new IndexOutOfBoundsException("非法索引:" + index + "总数："
					+ getCount());
		Assert.isTrue(indexOf(column) != -1, "不存在指定列:" + column);

		try {
			rowset.absolute(index + 1);
			if (c == String.class)
				return (T) rowset.getString(column);
			else if (c == Integer.class)
				return (T) new Integer(rowset.getInt(column));
			else if (c == Double.class)
				return (T) new Double(rowset.getDouble(column));
			else if (c == Float.class)
				return (T) new Double(rowset.getFloat(column));
			else if (c == Long.class)
				return (T) new Long(rowset.getLong(column));
			else if (c == Boolean.class)
				return (T) new Boolean(rowset.getBoolean(column));
			else if (c == Date.class)
				return (T) new Date(rowset.getDate(column).getTime());
			else if (c == Short.class)
				return (T) new Short(rowset.getShort(column));
			else if (c == BigDecimal.class)
				return (T) rowset.getBigDecimal(column);
			else if (c == Object.class)
				return (T) rowset.getObject(column);
			else { // 使用默认转换器进行转换
				return (T) rowset.getObject(column);
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void setParam(int index, Object value) {
		if (params.length <= index) {
			Object[] newParams = new Object[index + 1];
			System.arraycopy(params, 0, newParams, 0, params.length);
			params = newParams;
		}
		params[index] = value;
	}

	/**
	 * 根据表达示获取字符文本 ${time,yyyy-MM-dd}
	 */
	public String getStringForExpr(int index, String expr) {
		Assert.isNotNull(expr);
		if (rowset == null)
			return null;
		if (index < 0 || index >= getCount())
			throw new IndexOutOfBoundsException("非法索引:" + index + "总数："
					+ getCount());

		Pattern p = Pattern.compile("\\$\\{.*?\\}");
		Matcher matcher = p.matcher(expr);
		StringBuffer result = new StringBuffer();
		int end = 0;
		while (matcher.find()) {
			String exprItem = matcher.group();
			String columnDescr = exprItem.substring(2, exprItem.length() - 1);
			int spit = columnDescr.indexOf(",");
			String column, format = null;
			if (spit != -1) {
				column = columnDescr.substring(0, spit);
				format = columnDescr.substring(spit + 1);
			} else {
				column = columnDescr;
			}
			if (indexOf(column) != -1) {
				result.append(expr.subSequence(end, matcher.start()));
				result.append(toString(getValueForIndex(index, column), format));
			}
			end = matcher.end();
		}
		result.append(expr.subSequence(end, expr.length()));
		return result.toString();
	}

	public String getStringForExpr(String expr) {
		if (selectedIndex == -1)
			return null;
		return getStringForExpr(selectedIndex, expr);
	}

	public String getCommand() {
		if (sysSource) {
			DatasetDefine define = provide.getDataset(defineKey);
			return define.getCommand();
		}
		return command;
	}

	public String getDefineKey() {
		return defineKey;
	}

	public IDataBaseProvide getProvide() {
		return provide;
	}

	public void clear() {
		if (rowset != null) {
			rowset = null;
			fireDatasetEvent(ROW_SET_CHANGED, new EventObject(this));
		}
	}
}
